﻿// Copyright 2012 T4C: sejerpz@gmail.com
// Licensed under GPL v3

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using SET.Core;

namespace SET.Excercises
{
    class Intervals
    {
        private int lowerOctave = 3;
        private int upperOctave = 7;

        private Random random;
        private PlayMidi midi;

        private Interval lastPlayedInterval = null;
        private int correctResponse = -1;

        public Direction[] Directions { get; set; }

        public int[] Distances { get; set; }

        public event EventHandler<NoteIntervalEventArgs> IntervalPlayed;
        public event EventHandler IntervalPlaying;

        public int CorrectResponse
        {
            get
            {
                return this.correctResponse;
            }
        }

        public Interval LastPlayedInterval
        {
            get
            {
                return this.lastPlayedInterval;
            }

        }
        public void Start()
        {
            lock (this)
            {
                midi = new PlayMidi();
            }
            random = new Random((int)(DateTime.Now.Ticks & 0xFFFFFFFF));
            PlayNew();
        }


        public void Stop()
        {
            lock (this)
            {
                midi = null;
            }
            random = null;
        }


        public bool EvaluateResponse(int interval)
        {
            return interval == this.correctResponse;
        }

        public void RepeatLast()
        {
            if (this.lastPlayedInterval == null)
                return;

            Play(this.lastPlayedInterval);
        }

        public void PlayNew()
        {
            if (midi == null)
                throw new Exception("Excercise is not started");

            int octave1 = 0;
            int octave2;
            int interval = 0;
            int note1 = 0;
            int note2;
            Direction direction = Direction.Ascending;

            do
            {
                octave1 = random.Next(lowerOctave, upperOctave + 1);
                note1 = random.Next(0, Notes.Names.Length);
            }
            while (lastPlayedInterval != null && octave1 == lastPlayedInterval.Octave1 && Notes.Names[note1] == lastPlayedInterval.Note1);
           
            interval = Distances[random.Next(0, Distances.Length)];
            direction = Directions[random.Next(0, Directions.Length)];

            octave2 = octave1;
            if (direction == Direction.Ascending)
            {
                note2 = note1 + interval;
                if (note2 >= 12)
                {
                    note2 -= 12;
                    octave2 += 1;
                }
                if (octave2 > upperOctave)
                {
                    octave1 = upperOctave - 1;
                    octave2 = upperOctave;
                }
            }
            else
            {
                note2 = note1 - interval;
                if (note2 < 0)
                {
                    note2 += 12;
                    octave2 -= 1;
                }
                if (octave2 < lowerOctave)
                {
                    octave2 = lowerOctave;
                    octave1 = lowerOctave + 1;
                }
            }

            correctResponse = interval;
            Play(new Interval(octave1, Notes.Names[note1], octave2, Notes.Names[note2]));
        }

        private void Play(Interval interval)
        {
            this.lastPlayedInterval = interval;

            var thread = new Thread(new ParameterizedThreadStart(this.PlayIntervalThread));
            thread.Start(interval);
            Thread.Sleep(0);
        }

        private void PlayIntervalThread(object state)
        {
            var interval = (Interval)state;
            bool played = false;

            Thread.Sleep(250); // some delay
            OnIntervalPlaying();
            lock (this)
            {
                if (midi != null)
                {
                    midi.PlayNote(interval.Octave1, interval.Note1);
                    midi.PlayNote(interval.Octave2, interval.Note2);
                    played = true;
                }
            }

            if (played)
                OnIntervalPlayed(new NoteIntervalEventArgs(interval));
        }

        private void OnIntervalPlaying()
        {
            if (IntervalPlaying != null)
                IntervalPlaying(this, EventArgs.Empty);
        }

        private void OnIntervalPlayed(NoteIntervalEventArgs noteIntervalEventArgs)
        {
            if (IntervalPlayed != null)
                IntervalPlayed(this, noteIntervalEventArgs);
        }
    }

    public enum Direction
    {
        Ascending,
        Descending
    }
}
